home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 7 / Apprentice-Release7.iso / Source Code / C / Applications / Tcl-Tk 8.0 / Pre-installed version / tk8.0 / generic / tkFocus.c < prev    next >
Encoding:
C/C++ Source or Header  |  1997-08-15  |  25.8 KB  |  849 lines  |  [TEXT/CWIE]

  1. /* 
  2.  * tkFocus.c --
  3.  *
  4.  *    This file contains procedures that manage the input
  5.  *    focus for Tk.
  6.  *
  7.  * Copyright (c) 1990-1994 The Regents of the University of California.
  8.  * Copyright (c) 1994-1997 Sun Microsystems, Inc.
  9.  *
  10.  * See the file "license.terms" for information on usage and redistribution
  11.  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
  12.  *
  13.  * SCCS: @(#) tkFocus.c 1.45 97/08/07 14:39:47
  14.  */
  15.  
  16. #include "tkInt.h"
  17. #include "tkPort.h"
  18.  
  19.  
  20. /*
  21.  * For each top-level window that has ever received the focus, there
  22.  * is a record of the following type:
  23.  */
  24.  
  25. typedef struct TkFocusInfo {
  26.     TkWindow *topLevelPtr;    /* Information about top-level window. */
  27.     TkWindow *focusWinPtr;    /* The next time the focus comes to this
  28.                  * top-level, it will be given to this
  29.                  * window. */
  30.     struct TkFocusInfo *nextPtr;/* Next in list of all focus records for
  31.                  * a given application. */
  32. } FocusInfo;
  33.  
  34. /*
  35.  * Global used for debugging.
  36.  */
  37. int tclFocusDebug = 0;
  38.  
  39. /*
  40.  * The following magic value is stored in the "send_event" field of
  41.  * FocusIn and FocusOut events that are generated in this file.  This
  42.  * allows us to separate "real" events coming from the server from
  43.  * those that we generated.
  44.  */
  45.  
  46. #define GENERATED_EVENT_MAGIC ((Bool) 0x547321ac)
  47.  
  48. /*
  49.  * Forward declarations for procedures defined in this file:
  50.  */
  51.  
  52.  
  53. static void        FocusMapProc _ANSI_ARGS_((ClientData clientData,
  54.                 XEvent *eventPtr));
  55. static TkWindow *    GetFocus _ANSI_ARGS_((TkWindow *winPtr));
  56. static void        GenerateFocusEvents _ANSI_ARGS_((TkWindow *sourcePtr,
  57.                 TkWindow *destPtr));
  58. static void        SetFocus _ANSI_ARGS_((TkWindow *winPtr, int force));
  59.  
  60. /*
  61.  *--------------------------------------------------------------
  62.  *
  63.  * Tk_FocusCmd --
  64.  *
  65.  *    This procedure is invoked to process the "focus" Tcl command.
  66.  *    See the user documentation for details on what it does.
  67.  *
  68.  * Results:
  69.  *    A standard Tcl result.
  70.  *
  71.  * Side effects:
  72.  *    See the user documentation.
  73.  *
  74.  *--------------------------------------------------------------
  75.  */
  76.  
  77. int
  78. Tk_FocusCmd(clientData, interp, argc, argv)
  79.     ClientData clientData;    /* Main window associated with
  80.                  * interpreter. */
  81.     Tcl_Interp *interp;        /* Current interpreter. */
  82.     int argc;            /* Number of arguments. */
  83.     char **argv;        /* Argument strings. */
  84. {
  85.     Tk_Window tkwin = (Tk_Window) clientData;
  86.     TkWindow *winPtr = (TkWindow *) clientData;
  87.     TkWindow *newPtr, *focusWinPtr, *topLevelPtr;
  88.     FocusInfo *focusPtr;
  89.     char c;
  90.     size_t length;
  91.  
  92.     /*
  93.      * If invoked with no arguments, just return the current focus window.
  94.      */
  95.  
  96.     if (argc == 1) {
  97.     focusWinPtr = GetFocus(winPtr);
  98.     if (focusWinPtr != NULL) {
  99.         interp->result = focusWinPtr->pathName;
  100.     }
  101.     return TCL_OK;
  102.     }
  103.  
  104.     /*
  105.      * If invoked with a single argument beginning with "." then focus
  106.      * on that window.
  107.      */
  108.  
  109.     if (argc == 2) {
  110.     if (argv[1][0] == 0) {
  111.         return TCL_OK;
  112.     }
  113.     if (argv[1][0] == '.') {
  114.         newPtr = (TkWindow *) Tk_NameToWindow(interp, argv[1], tkwin);
  115.         if (newPtr == NULL) {
  116.         return TCL_ERROR;
  117.         }
  118.         if (!(newPtr->flags & TK_ALREADY_DEAD)) {
  119.         SetFocus(newPtr, 0);
  120.         }
  121.         return TCL_OK;
  122.     }
  123.     }
  124.  
  125.     length = strlen(argv[1]);
  126.     c = argv[1][1];
  127.     if ((c == 'd') && (strncmp(argv[1], "-displayof", length) == 0)) {
  128.     if (argc != 3) {
  129.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  130.             argv[0], " -displayof window\"", (char *) NULL);
  131.         return TCL_ERROR;
  132.     }
  133.     newPtr = (TkWindow *) Tk_NameToWindow(interp, argv[2], tkwin);
  134.     if (newPtr == NULL) {
  135.         return TCL_ERROR;
  136.     }
  137.     newPtr = GetFocus(newPtr);
  138.     if (newPtr != NULL) {
  139.         interp->result = newPtr->pathName;
  140.     }
  141.     } else if ((c == 'f') && (strncmp(argv[1], "-force", length) == 0)) {
  142.     if (argc != 3) {
  143.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  144.             argv[0], " -force window\"", (char *) NULL);
  145.         return TCL_ERROR;
  146.     }
  147.     if (argv[2][0] == 0) {
  148.         return TCL_OK;
  149.     }
  150.     newPtr = (TkWindow *) Tk_NameToWindow(interp, argv[2], tkwin);
  151.     if (newPtr == NULL) {
  152.         return TCL_ERROR;
  153.     }
  154.     SetFocus(newPtr, 1);
  155.     } else if ((c == 'l') && (strncmp(argv[1], "-lastfor", length) == 0)) {
  156.     if (argc != 3) {
  157.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  158.             argv[0], " -lastfor window\"", (char *) NULL);
  159.         return TCL_ERROR;
  160.     }
  161.     newPtr = (TkWindow *) Tk_NameToWindow(interp, argv[2], tkwin);
  162.     if (newPtr == NULL) {
  163.         return TCL_ERROR;
  164.     }
  165.     for (topLevelPtr = newPtr; topLevelPtr != NULL;
  166.         topLevelPtr = topLevelPtr->parentPtr)  {
  167.         if (topLevelPtr->flags & TK_TOP_LEVEL) {
  168.         for (focusPtr = newPtr->mainPtr->focusPtr; focusPtr != NULL;
  169.             focusPtr = focusPtr->nextPtr) {
  170.             if (focusPtr->topLevelPtr == topLevelPtr) {
  171.             interp->result = focusPtr->focusWinPtr->pathName;
  172.             return TCL_OK;
  173.             }
  174.         }
  175.         interp->result = topLevelPtr->pathName;
  176.         return TCL_OK;
  177.         }
  178.     }
  179.     } else {
  180.     Tcl_AppendResult(interp, "bad option \"", argv[1],
  181.         "\": must be -displayof, -force, or -lastfor", (char *) NULL);
  182.     return TCL_ERROR;
  183.     }
  184.     return TCL_OK;
  185. }
  186.  
  187. /*
  188.  *--------------------------------------------------------------
  189.  *
  190.  * TkFocusFilterEvent --
  191.  *
  192.  *    This procedure is invoked by Tk_HandleEvent when it encounters
  193.  *    a FocusIn, FocusOut, Enter, or Leave event.
  194.  *
  195.  * Results:
  196.  *    A return value of 1 means that Tk_HandleEvent should process
  197.  *    the event normally (i.e. event handlers should be invoked).
  198.  *    A return value of 0 means that this event should be ignored.
  199.  *
  200.  * Side effects:
  201.  *    Additional events may be generated, and the focus may switch.
  202.  *
  203.  *--------------------------------------------------------------
  204.  */
  205.  
  206. int
  207. TkFocusFilterEvent(winPtr, eventPtr)
  208.     TkWindow *winPtr;        /* Window that focus event is directed to. */
  209.     XEvent *eventPtr;        /* FocusIn, FocusOut, Enter, or Leave
  210.                  * event. */
  211. {
  212.     /*
  213.      * Design notes: the window manager and X server work together to
  214.      * transfer the focus among top-level windows.  This procedure takes
  215.      * care of transferring the focus from a top-level or wrapper window
  216.      * to the actual window within that top-level that has the focus. 
  217.      * We do this by synthesizing X events to move the focus around. 
  218.      * None of the FocusIn and FocusOut events generated by X are ever
  219.      * used outside of this procedure;  only the synthesized events get
  220.      * through to the rest of the application.  At one point (e.g.
  221.      * Tk4.0b1) Tk used to call X to move the focus from a top-level to
  222.      * one of its descendants, then just pass through the events
  223.      * generated by X. This approach didn't work very well, for a
  224.      * variety of reasons. For example, if X generates the events they
  225.      * go at the back of the event queue, which could cause problems if
  226.      * other things have already happened, such as moving the focus to
  227.      * yet another window.
  228.      */
  229.  
  230.     FocusInfo *focusPtr;
  231.     TkDisplay *dispPtr = winPtr->dispPtr;
  232.     TkWindow *newFocusPtr;
  233.     int retValue, delta;
  234.  
  235.     /*
  236.      * If this was a generated event, just turn off the generated
  237.      * flag and pass the event through to Tk bindings.
  238.      */
  239.  
  240.     if (eventPtr->xfocus.send_event == GENERATED_EVENT_MAGIC) {
  241.     eventPtr->xfocus.send_event = 0;
  242.     return 1;
  243.     }
  244.  
  245.     /*
  246.      * Check for special events generated by embedded applications to
  247.      * request the input focus.  If this is one of those events, make
  248.      * the change in focus and return without any additional processing
  249.      * of the event (note: the "detail" field of the event indicates
  250.      * whether to claim the focus even if we don't already have it).
  251.      */
  252.  
  253.     if ((eventPtr->xfocus.mode == EMBEDDED_APP_WANTS_FOCUS)
  254.         && (eventPtr->type == FocusIn)) {
  255.     SetFocus(winPtr, eventPtr->xfocus.detail);
  256.     return 0;
  257.     }
  258.  
  259.     /*
  260.      * This was not a generated event.  We'll return 1 (so that the
  261.      * event will be processed) if it's an Enter or Leave event, and
  262.      * 0 (so that the event won't be processed) if it's a FocusIn or
  263.      * FocusOut event.
  264.      */
  265.  
  266.     retValue = 0;
  267.     if (eventPtr->type == FocusIn) {
  268.     /*
  269.      * Skip FocusIn events that cause confusion
  270.      * NotifyVirtual and NotifyNonlinearVirtual - Virtual events occur
  271.      *    on windows in between the origin and destination of the
  272.      *    focus change.  For FocusIn we may see this when focus
  273.      *    goes into an embedded child.  We don't care about this,
  274.      *    although we may end up getting a NotifyPointer later.
  275.      * NotifyInferior - focus is coming to us from an embedded child.
  276.      *    When focus is on an embeded focus, we still think we have
  277.      *    the focus, too, so this message doesn't change our state.
  278.      * NotifyPointerRoot - should never happen because this is sent
  279.      *    to the root window.
  280.      *
  281.      * Interesting FocusIn events are
  282.      * NotifyAncestor - focus is coming from our parent, probably the root.
  283.      * NotifyNonlinear - focus is coming from a different branch, probably
  284.      *    another toplevel.
  285.      * NotifyPointer - implicit focus because of the mouse position.
  286.      *    This is    only interesting on toplevels, when it means that the
  287.      *    focus has been set to the root window but the mouse is over
  288.      *    this toplevel.  We take the focus implicitly (probably no
  289.      *    window manager)
  290.      */
  291.  
  292.     if ((eventPtr->xfocus.detail == NotifyVirtual)
  293.         || (eventPtr->xfocus.detail == NotifyNonlinearVirtual)
  294.         || (eventPtr->xfocus.detail == NotifyPointerRoot)
  295.         || (eventPtr->xfocus.detail == NotifyInferior)) {
  296.         return retValue;
  297.     }
  298.     } else if (eventPtr->type == FocusOut) {
  299.     /*
  300.      * Skip FocusOut events that cause confusion.
  301.      * NotifyPointer - the pointer is in us or a child, and we are loosing
  302.      *    focus because of an XSetInputFocus.  Other focus events
  303.      *    will set our state properly.
  304.      * NotifyPointerRoot - should never happen because this is sent
  305.      *    to the root window.
  306.      * NotifyInferior - focus leaving us for an embedded child.  We
  307.      *    retain a notion of focus when an embedded child has focus.
  308.      *
  309.      * Interesting events are:
  310.      * NotifyAncestor - focus is going to root.
  311.      * NotifyNonlinear - focus is going to another branch, probably
  312.      *    another toplevel.
  313.      * NotifyVirtual, NotifyNonlinearVirtual - focus is passing through,
  314.      *    and we need to make sure we track this.
  315.      */
  316.  
  317.     if ((eventPtr->xfocus.detail == NotifyPointer)
  318.         || (eventPtr->xfocus.detail == NotifyPointerRoot)
  319.         || (eventPtr->xfocus.detail == NotifyInferior)) {
  320.         return retValue;
  321.     }
  322.     } else {
  323.     retValue = 1;
  324.     if (eventPtr->xcrossing.detail == NotifyInferior) {
  325.         return retValue;
  326.     }
  327.     }
  328.  
  329.     /*
  330.      * If winPtr isn't a top-level window than just ignore the event.
  331.      */
  332.  
  333.     winPtr = TkWmFocusToplevel(winPtr);
  334.     if (winPtr == NULL) {
  335.     return retValue;
  336.     }
  337.  
  338.     /*
  339.      * If there is a grab in effect and this window is outside the
  340.      * grabbed tree, then ignore the event.
  341.      */
  342.  
  343.     if (TkGrabState(winPtr) == TK_GRAB_EXCLUDED)  {
  344.     return retValue;
  345.     }
  346.  
  347.     /*
  348.      * It is possible that there were outstanding FocusIn and FocusOut
  349.      * events on their way to us at the time the focus was changed
  350.      * internally with the "focus" command.  If so, these events could
  351.      * potentially cause us to lose the focus (switch it to the window
  352.      * of the last FocusIn event) even though the focus change occurred
  353.      * after those events.  The following code detects this and ignores
  354.      * the stale events.
  355.      *
  356.      * Note: the focusSerial is only generated by TkpChangeFocus,
  357.      * whereas in Tk 4.2 there was always a nop marker generated.
  358.      */
  359.  
  360.     delta = eventPtr->xfocus.serial - winPtr->mainPtr->focusSerial;
  361.     if (delta < 0) {
  362.     return retValue;
  363.     }
  364.  
  365.     /*
  366.      * Find the FocusInfo structure for the window, and make a new one
  367.      * if there isn't one already.
  368.      */
  369.  
  370.     for (focusPtr = winPtr->mainPtr->focusPtr; focusPtr != NULL;
  371.         focusPtr = focusPtr->nextPtr) {
  372.     if (focusPtr->topLevelPtr == winPtr) {
  373.         break;
  374.     }
  375.     }
  376.     if (focusPtr == NULL) {
  377.     focusPtr = (FocusInfo *) ckalloc(sizeof(FocusInfo));
  378.     focusPtr->topLevelPtr = focusPtr->focusWinPtr = winPtr;
  379.     focusPtr->nextPtr = winPtr->mainPtr->focusPtr;
  380.     winPtr->mainPtr->focusPtr = focusPtr;
  381.     }
  382.     newFocusPtr = focusPtr->focusWinPtr;
  383.  
  384.     if (eventPtr->type == FocusIn) {
  385.     GenerateFocusEvents(winPtr->mainPtr->focusWinPtr, newFocusPtr);
  386.     winPtr->mainPtr->focusWinPtr = newFocusPtr;
  387.  
  388.     /*
  389.      * NotifyPointer gets set when the focus has been set to the root window
  390.      * but we have the pointer.  We'll treat this like an implicit
  391.      * focus in event so that upon Leave events we release focus.
  392.      */
  393.  
  394.     if (eventPtr->xfocus.detail == NotifyPointer) {
  395.         dispPtr->implicitWinPtr = winPtr;
  396.     } else {
  397.         dispPtr->implicitWinPtr = NULL;
  398.     }
  399.     } else if (eventPtr->type == FocusOut) {
  400.     GenerateFocusEvents(winPtr->mainPtr->focusWinPtr, (TkWindow *) NULL);
  401.     winPtr->mainPtr->focusWinPtr = NULL;
  402.     } else if (eventPtr->type == EnterNotify) {
  403.     /*
  404.      * If there is no window manager, or if the window manager isn't
  405.      * moving the focus around (e.g. the disgusting "NoTitleFocus"
  406.      * option has been selected in twm), then we won't get FocusIn
  407.      * or FocusOut events.  Instead, the "focus" field will be set
  408.      * in an Enter event to indicate that we've already got the focus
  409.      * when the mouse enters the window (even though we didn't get
  410.      * a FocusIn event).  Watch for this and grab the focus when it
  411.      * happens.  Note: if this is an embedded application then don't
  412.      * accept the focus implicitly like this;  the container
  413.      * application will give us the focus explicitly if it wants us
  414.      * to have it.
  415.      */
  416.  
  417.     if (eventPtr->xcrossing.focus &&
  418.                 (winPtr->mainPtr->focusWinPtr == NULL)
  419.         && !(winPtr->flags & TK_EMBEDDED)) {
  420.         if (tclFocusDebug) {
  421.         printf("Focussed implicitly on %s\n",
  422.             newFocusPtr->pathName);
  423.         }
  424.  
  425.         GenerateFocusEvents(winPtr->mainPtr->focusWinPtr, newFocusPtr);
  426.         winPtr->mainPtr->focusWinPtr = newFocusPtr;
  427.         dispPtr->implicitWinPtr = winPtr;
  428.     }
  429.     } else if (eventPtr->type == LeaveNotify) {
  430.     /*
  431.      * If the pointer just left a window for which we automatically
  432.      * claimed the focus on enter, move the focus back to the root
  433.      * window, where it was before we claimed it above.  Note:
  434.      * dispPtr->implicitWinPtr may not be the same as
  435.      * winPtr->mainPtr->focusWinPtr (e.g. because the "focus"
  436.      * command was used to redirect the focus after it arrived at
  437.      * dispPtr->implicitWinPtr)!!  In addition, we generate events
  438.      * because the window manager won't give us a FocusOut event when
  439.      * we focus on the root. 
  440.      */
  441.  
  442.     if (dispPtr->implicitWinPtr != NULL) {
  443.         if (tclFocusDebug) {
  444.         printf("Defocussed implicit Async\n");
  445.         }
  446.         GenerateFocusEvents(winPtr->mainPtr->focusWinPtr, (TkWindow *) NULL);
  447.         XSetInputFocus(dispPtr->display, PointerRoot, RevertToPointerRoot,
  448.             CurrentTime);
  449.         winPtr->mainPtr->focusWinPtr = NULL;
  450.         dispPtr->implicitWinPtr = NULL;
  451.     }
  452.     }
  453.     return retValue;
  454. }
  455.  
  456. /*
  457.  *----------------------------------------------------------------------
  458.  *
  459.  * SetFocus --
  460.  *
  461.  *    This procedure is invoked to change the focus window for a
  462.  *    given display in a given application.
  463.  *
  464.  * Results:
  465.  *    None.
  466.  *
  467.  * Side effects:
  468.  *    Event handlers may be invoked to process the change of
  469.  *    focus.
  470.  *
  471.  *----------------------------------------------------------------------
  472.  */
  473.  
  474. static void
  475. SetFocus(winPtr, force)
  476.     TkWindow *winPtr;        /* Window that is to be the new focus for
  477.                  * its display and application. */
  478.     int force;            /* If non-zero, set the X focus to this
  479.                  * window even if the application doesn't
  480.                  * currently have the X focus. */
  481. {
  482.     FocusInfo *focusPtr;
  483.     TkWindow *topLevelPtr;
  484.     int allMapped;
  485.  
  486.     if (winPtr == winPtr->mainPtr->focusWinPtr) {
  487.     return;
  488.     }
  489.  
  490.     /*
  491.      * Find the top-level window for winPtr, then find (or create)
  492.      * a record for the top-level.  Also see whether winPtr and all its
  493.      * ancestors are mapped.
  494.      */
  495.  
  496.     allMapped = 1;
  497.     for (topLevelPtr = winPtr; ; topLevelPtr = topLevelPtr->parentPtr)  {
  498.     if (topLevelPtr == NULL) {
  499.         /*
  500.          * The window is being deleted.  No point in worrying about
  501.          * giving it the focus.
  502.          */
  503.         return;
  504.     }
  505.     if (!(topLevelPtr->flags & TK_MAPPED)) {
  506.         allMapped = 0;
  507.     }
  508.     if (topLevelPtr->flags & TK_TOP_LEVEL) {
  509.         break;
  510.     }
  511.     }
  512.  
  513.     /*
  514.      * If the new focus window isn't mapped, then we can't focus on it
  515.      * (X will generate an error, for example).  Instead, create an
  516.      * event handler that will set the focus to this window once it gets
  517.      * mapped.  At the same time, delete any old handler that might be
  518.      * around;  it's no longer relevant.
  519.      */
  520.  
  521.     if (winPtr->mainPtr->focusOnMapPtr != NULL) {
  522.     Tk_DeleteEventHandler(
  523.         (Tk_Window) winPtr->mainPtr->focusOnMapPtr,
  524.         StructureNotifyMask, FocusMapProc,
  525.         (ClientData) winPtr->mainPtr->focusOnMapPtr);
  526.     winPtr->mainPtr->focusOnMapPtr = NULL;
  527.     }
  528.     if (!allMapped) {
  529.     Tk_CreateEventHandler((Tk_Window) winPtr,
  530.         VisibilityChangeMask, FocusMapProc,
  531.         (ClientData) winPtr);
  532.     winPtr->mainPtr->focusOnMapPtr = winPtr;
  533.     winPtr->mainPtr->forceFocus = force;
  534.     return;
  535.     }
  536.  
  537.     for (focusPtr = winPtr->mainPtr->focusPtr; focusPtr != NULL;
  538.         focusPtr = focusPtr->nextPtr) {
  539.     if (focusPtr->topLevelPtr == topLevelPtr) {
  540.         break;
  541.     }
  542.     }
  543.     if (focusPtr == NULL) {
  544.     focusPtr = (FocusInfo *) ckalloc(sizeof(FocusInfo));
  545.     focusPtr->topLevelPtr = topLevelPtr;
  546.     focusPtr->nextPtr = winPtr->mainPtr->focusPtr;
  547.     winPtr->mainPtr->focusPtr = focusPtr;
  548.     }
  549.     focusPtr->focusWinPtr = winPtr;
  550.  
  551.     /*
  552.      * Reset the window system's focus window and generate focus events,
  553.      * with two special cases:
  554.      *
  555.      * 1. If the application is embedded and doesn't currently have the
  556.      *    focus.  Don't set the focus directly.  Instead, see if the
  557.      *    embedding code can claim the focus from the enclosing
  558.      *    container.
  559.      * 2. Otherwise, if the application doesn't currently have the
  560.      *    focus, don't change the window system's focus unless it was
  561.      *    already in this application or "force" was specified.
  562.      */
  563.  
  564.     if ((topLevelPtr->flags & TK_EMBEDDED)
  565.         && (winPtr->mainPtr->focusWinPtr == NULL)) {
  566.     TkpClaimFocus(topLevelPtr, force);
  567.     } else if ((winPtr->mainPtr->focusWinPtr != NULL) || force) {
  568.     /*
  569.      * Generate events to shift focus between Tk windows.
  570.      * We do this regardless of what TkpChangeFocus does with
  571.      * the real X focus so that Tk widgets track focus commands
  572.      * when there is no window manager.  GenerateFocusEvents will
  573.      * set up a serial number marker so we discard focus events
  574.      * that are triggered by the ChangeFocus.
  575.      */
  576.  
  577.     TkpChangeFocus(TkpGetWrapperWindow(topLevelPtr), force);
  578.     GenerateFocusEvents(winPtr->mainPtr->focusWinPtr, winPtr);
  579.     winPtr->mainPtr->focusWinPtr = winPtr;
  580.     }
  581. }
  582.  
  583. /*
  584.  *----------------------------------------------------------------------
  585.  *
  586.  * GetFocus --
  587.  *
  588.  *    Given a window, this procedure returns the current focus
  589.  *    window for its application and display.
  590.  *
  591.  * Results:
  592.  *    The return value is a pointer to the window that currently
  593.  *    has the input focus for the specified application and
  594.  *    display, or NULL if none.
  595.  *
  596.  * Side effects:
  597.  *    None.
  598.  *
  599.  *----------------------------------------------------------------------
  600.  */
  601.  
  602. static TkWindow *
  603. GetFocus(winPtr)
  604.     TkWindow *winPtr;        /* Window that selects an application
  605.                  * and a display. */
  606. {
  607.     TkWindow *focusWinPtr;
  608.  
  609.     focusWinPtr = winPtr->mainPtr->focusWinPtr;
  610.     if ((focusWinPtr != NULL) && (focusWinPtr->mainPtr == winPtr->mainPtr)) {
  611.     return focusWinPtr;
  612.     }
  613.     return (TkWindow *) NULL;
  614. }
  615.  
  616. /*
  617.  *----------------------------------------------------------------------
  618.  *
  619.  * TkFocusKeyEvent --
  620.  *
  621.  *    Given a window and a key press or release event that arrived for
  622.  *    the window, use information about the keyboard focus to compute
  623.  *    which window should really get the event.  In addition, update
  624.  *    the event to refer to its new window.
  625.  *
  626.  * Results:
  627.  *    The return value is a pointer to the window that has the input
  628.  *    focus in winPtr's application, or NULL if winPtr's application
  629.  *    doesn't have the input focus.  If a non-NULL value is returned,
  630.  *    eventPtr will be updated to refer properly to the focus window.
  631.  *
  632.  * Side effects:
  633.  *    None.
  634.  *
  635.  *----------------------------------------------------------------------
  636.  */
  637.  
  638. TkWindow *
  639. TkFocusKeyEvent(winPtr, eventPtr)
  640.     TkWindow *winPtr;        /* Window that selects an application
  641.                  * and a display. */
  642.     XEvent *eventPtr;        /* X event to redirect (should be KeyPress
  643.                  * or KeyRelease). */
  644. {
  645.     TkWindow *focusWinPtr;
  646.     int focusX, focusY, vRootX, vRootY, vRootWidth, vRootHeight;
  647.  
  648.     focusWinPtr = winPtr->mainPtr->focusWinPtr;
  649.     if ((focusWinPtr != NULL) && (focusWinPtr->mainPtr == winPtr->mainPtr)) {
  650.     /*
  651.      * Map the x and y coordinates to make sense in the context of
  652.      * the focus window, if possible (make both -1 if the map-from
  653.      * and map-to windows don't share the same screen).
  654.      */
  655.  
  656.     if ((focusWinPtr->display != winPtr->display)
  657.         || (focusWinPtr->screenNum != winPtr->screenNum)) {
  658.         eventPtr->xkey.x = -1;
  659.         eventPtr->xkey.y = -1;
  660.     } else {
  661.         Tk_GetVRootGeometry((Tk_Window) focusWinPtr, &vRootX, &vRootY,
  662.             &vRootWidth, &vRootHeight);
  663.         Tk_GetRootCoords((Tk_Window) focusWinPtr, &focusX, &focusY);
  664.         eventPtr->xkey.x = eventPtr->xkey.x_root - vRootX - focusX;
  665.         eventPtr->xkey.y = eventPtr->xkey.y_root - vRootY - focusY;
  666.     }
  667.     eventPtr->xkey.window = focusWinPtr->window;
  668.     return focusWinPtr;
  669.     }
  670.  
  671.     /*
  672.      * The event doesn't belong to us.  Perhaps, due to embedding, it
  673.      * really belongs to someone else.  Give the embedding code a chance
  674.      * to redirect the event.
  675.      */
  676.  
  677.     TkpRedirectKeyEvent(winPtr, eventPtr);
  678.     return (TkWindow *) NULL;
  679. }
  680.  
  681. /*
  682.  *----------------------------------------------------------------------
  683.  *
  684.  * TkFocusDeadWindow --
  685.  *
  686.  *    This procedure is invoked when it is determined that
  687.  *    a window is dead.  It cleans up focus-related information
  688.  *    about the window.
  689.  *
  690.  * Results:
  691.  *    None.
  692.  *
  693.  * Side effects:
  694.  *    Various things get cleaned up and recycled.
  695.  *
  696.  *----------------------------------------------------------------------
  697.  */
  698.  
  699. void
  700. TkFocusDeadWindow(winPtr)
  701.     register TkWindow *winPtr;        /* Information about the window
  702.                      * that is being deleted. */
  703. {
  704.     FocusInfo *focusPtr, *prevPtr;
  705.     TkDisplay *dispPtr = winPtr->dispPtr;
  706.  
  707.     /*
  708.      * Search for focus records that refer to this window either as
  709.      * the top-level window or the current focus window.
  710.      */
  711.  
  712.     for (prevPtr = NULL, focusPtr = winPtr->mainPtr->focusPtr;
  713.         focusPtr != NULL;
  714.         prevPtr = focusPtr, focusPtr = focusPtr->nextPtr) {
  715.     if (winPtr == focusPtr->topLevelPtr) {
  716.         /*
  717.          * The top-level window is the one being deleted: free
  718.          * the focus record and release the focus back to PointerRoot
  719.          * if we acquired it implicitly.
  720.          */
  721.  
  722.         if (dispPtr->implicitWinPtr == winPtr) {
  723.         if (tclFocusDebug) {
  724.             printf("releasing focus to root after %s died\n",
  725.                 focusPtr->topLevelPtr->pathName);
  726.         }
  727.         dispPtr->implicitWinPtr = NULL;
  728.         winPtr->mainPtr->focusWinPtr = NULL;
  729.         }
  730.         if (winPtr->mainPtr->focusWinPtr == focusPtr->focusWinPtr) {
  731.         winPtr->mainPtr->focusWinPtr = NULL;
  732.         }
  733.         if (prevPtr == NULL) {
  734.         winPtr->mainPtr->focusPtr = focusPtr->nextPtr;
  735.         } else {
  736.         prevPtr->nextPtr = focusPtr->nextPtr;
  737.         }
  738.         ckfree((char *) focusPtr);
  739.         break;
  740.     } else if (winPtr == focusPtr->focusWinPtr) {
  741.         /*
  742.          * The deleted window had the focus for its top-level:
  743.          * move the focus to the top-level itself.
  744.          */
  745.  
  746.         focusPtr->focusWinPtr = focusPtr->topLevelPtr;
  747.         if ((winPtr->mainPtr->focusWinPtr == winPtr)
  748.             && !(focusPtr->topLevelPtr->flags & TK_ALREADY_DEAD)) {
  749.         if (tclFocusDebug) {
  750.             printf("forwarding focus to %s after %s died\n",
  751.                 focusPtr->topLevelPtr->pathName, winPtr->pathName);
  752.         }
  753.         GenerateFocusEvents(winPtr->mainPtr->focusWinPtr,
  754.             focusPtr->topLevelPtr);
  755.         winPtr->mainPtr->focusWinPtr = focusPtr->topLevelPtr;
  756.         }
  757.         break;
  758.     }
  759.     }
  760.  
  761.     if (winPtr->mainPtr->focusOnMapPtr == winPtr) {
  762.     winPtr->mainPtr->focusOnMapPtr = NULL;
  763.     }
  764. }
  765.  
  766. /*
  767.  *----------------------------------------------------------------------
  768.  *
  769.  * GenerateFocusEvents --
  770.  *
  771.  *    This procedure is called to create FocusIn and FocusOut events to
  772.  *    move the input focus from one window to another.
  773.  *
  774.  * Results:
  775.  *    None.
  776.  *
  777.  * Side effects:
  778.  *    FocusIn and FocusOut events are generated.
  779.  *
  780.  *----------------------------------------------------------------------
  781.  */
  782.  
  783. static void
  784. GenerateFocusEvents(sourcePtr, destPtr)
  785.     TkWindow *sourcePtr;    /* Window that used to have the focus (may
  786.                  * be NULL). */
  787.     TkWindow *destPtr;        /* New window to have the focus (may be
  788.                  * NULL). */
  789.  
  790. {
  791.     XEvent event;
  792.     TkWindow *winPtr;
  793.  
  794.     winPtr = sourcePtr;
  795.     if (winPtr == NULL) {
  796.     winPtr = destPtr;
  797.     if (winPtr == NULL) {
  798.         return;
  799.     }
  800.     }
  801.  
  802.     event.xfocus.serial = LastKnownRequestProcessed(winPtr->display);
  803.     event.xfocus.send_event = GENERATED_EVENT_MAGIC;
  804.     event.xfocus.display = winPtr->display;
  805.     event.xfocus.mode = NotifyNormal;
  806.     TkInOutEvents(&event, sourcePtr, destPtr, FocusOut, FocusIn,
  807.         TCL_QUEUE_MARK);
  808. }
  809.  
  810. /*
  811.  *----------------------------------------------------------------------
  812.  *
  813.  * FocusMapProc --
  814.  *
  815.  *    This procedure is called as an event handler for VisibilityNotify
  816.  *    events, if a window receives the focus at a time when its
  817.  *    toplevel isn't mapped.  The procedure is needed because X
  818.  *    won't allow the focus to be set to an unmapped window;  we
  819.  *    detect when the toplevel is mapped and set the focus to it then.
  820.  *
  821.  * Results:
  822.  *    None.
  823.  *
  824.  * Side effects:
  825.  *    If this is a map event, the focus gets set to the toplevel
  826.  *    given by clientData.
  827.  *
  828.  *----------------------------------------------------------------------
  829.  */
  830.  
  831. static void
  832. FocusMapProc(clientData, eventPtr)
  833.     ClientData clientData;    /* Toplevel window. */
  834.     XEvent *eventPtr;        /* Information about event. */
  835. {
  836.     TkWindow *winPtr = (TkWindow *) clientData;
  837.  
  838.     if (eventPtr->type == VisibilityNotify) {
  839.     if (tclFocusDebug) {
  840.         printf("auto-focussing on %s, force %d\n", winPtr->pathName,
  841.             winPtr->mainPtr->forceFocus);
  842.     }
  843.     Tk_DeleteEventHandler((Tk_Window) winPtr, VisibilityChangeMask,
  844.         FocusMapProc, clientData);
  845.     winPtr->mainPtr->focusOnMapPtr = NULL;
  846.     SetFocus(winPtr, winPtr->mainPtr->forceFocus);
  847.     }
  848. }
  849.